﻿using DiffusionAnalyticalSolution;
using lopet.Repositories;
using MathNet.Numerics;
using MathNet.Numerics.Distributions;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace BinomialSimulatorSimulator
{
    class Program
    {
        static int GetBinomialRandomVariable(double p, int n)
        {
            if (p < 0)
            {
                //Console.WriteLine("warning p = {0}", p);
                p = 0;
            }
            var b = new Binomial(p, n);
            b.RandomSource = RandomNumberRepository.Instance.GetRandomClass();

            return b.Sample();
        }

        static void CalculateArrivingMoleculesBinom(ref Communication communication, Analytic analytic, double Ts, int NumberOfSentMolecules, int maxNumberOfSymbolDurationsToLookBack)
        {
            for (int i = 1; i < communication._bitValues.Length; i++)
            {
                int j;
                if (maxNumberOfSymbolDurationsToLookBack != 0)
                {
                    j = i - maxNumberOfSymbolDurationsToLookBack;

                    if (j < 0)
                    {
                        j = 0;
                    }
                }
                else
                {
                    j = 0;
                }

                double numberOfArrivals = 0;

                for (; j <= i - 1; j++)
                {
                    if (communication._bitValues[j] != 0)
                    {
                        var a = analytic.DifferenceOfMoleculesWithDecompositionConsidered((i - j - 1) * Ts, (i - j) * Ts);
                        numberOfArrivals += GetBinomialRandomVariable(a, NumberOfSentMolecules);
                    }
                }

                communication._fracValues[i - 1] = numberOfArrivals;

                if (i % 1000 == 0)
                {
                    Console.WriteLine(i);
                }
            }
        }

        static void RegularSimulatorEyeDiagram()
        {
            var halfLife = double.MaxValue;
            var numberOfMolecules = 1000;
            
            StreamWriter sw = new StreamWriter(String.Format("Logs\\Eye2\\5BinomSimulator D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}, halfLife = {6}.txt"
                , 14, numberOfMolecules, false, "0.01 - 0.1", 1000, 0, halfLife), false);

            for (double t = 0.01; t < 0.9; t += 0.01)
            {
                var distance = 14;
                var symbolDuration = t;
                var writingInterval = t / 1;
                var simduration = 1000 * (int)(symbolDuration / writingInterval); //bits
                
                var lambda = Math.Log(2) / halfLife;
                var lookbackBits = 0;


                var communication = new Communication(simduration);
                var analytic = new Analytic(10, distance, 79.4, lambda);
                communication.InitializeRandomForEyeDiagram((int)(symbolDuration / writingInterval));

                CalculateArrivingMoleculesBinom(ref communication, analytic, writingInterval, numberOfMolecules, lookbackBits);

                
                //for (int i = 0; i < communication._bitValues.Length; i = i + (int)(symbolDuration/writingInterval))
                //{
                //    var stBuilder = new StringBuilder();

                //    stBuilder.Append(communication._bitValues[i] + " ");

                //    for (int j = 0; j < (int)(symbolDuration/writingInterval); j++)
                //    {
                //        stBuilder.Append(communication._fracValues[i + j] + " ");
                //    }

                //    sw.WriteLine(stBuilder.ToString());
                //}

                var stringBuilder = new StringBuilder();

                for (int i = 0; i < communication._bitValues.Length; i++)
                {
                    stringBuilder.Append(communication._fracValues[i] + " ");
                }

                sw.WriteLine(stringBuilder.ToString());

                sw.Flush();
            }

            sw.Close();
        }

        static void RegularSimulator()
        {
            var hlArray = new double[] { 0.032, 0.064, 0.128, 0.256, 0.512, 1.024, double.MaxValue};

            for (int t = 0; t < hlArray.Length; t += 1)
            {
                for (int j = 64; j < 1000; j *= 2)
                {
                    var distance = j;
                    var symbolDuration = 10.000;
                    var simduration = 1000; //bits
                    var numberOfMolecules = 1000;
                    var halfLife = hlArray[t];
                    var lambda = Math.Log(2) / halfLife;
                    var lookbackBits = 0;

                    var communication = new Communication(simduration);
                    var analytic = new Analytic(10, distance, 79.4, lambda);
                    communication.InitializeRandom();

                    CalculateArrivingMoleculesBinom(ref communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

                    StreamWriter sw = new StreamWriter(String.Format("Logs\\1000\\BinomSimulator D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}, halfLife = {6}.txt", distance,
                       numberOfMolecules, false, symbolDuration, simduration, lookbackBits, halfLife), false);

                    for (int i = 0; i < communication._bitValues.Length; i++)
                    {
                        sw.WriteLine(communication._bitValues[i] + " " + communication._fracValues[i]);
                    }

                    sw.Flush();
                    sw.Close();
                }
                
            }
        }

        static void HistogramSimulator()
        {
            for (double t = 0.016; t <= 1.024; t *= 2)
            {
                var distance = 14;
                var symbolDuration = 0.001; // 10 msec bins
                var simduration = 10000; //how many bins are there
                var numberOfMolecules = 100000;
                var halfLife = t;
                var lambda = Math.Log(2) / halfLife;
                var lookbackBits = 0;

                var communication = new Communication(simduration);
                var analytic = new Analytic(10, distance, 79.4, lambda);
                communication.InitializeFirstOneRestZero();

                CalculateArrivingMoleculesBinom(ref communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

                StreamWriter sw = new StreamWriter(String.Format("Logs\\Histogram\\BinomSim D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}, halfLife = {6}.txt", distance,
                   numberOfMolecules, true, symbolDuration, simduration, lookbackBits, halfLife), false);

                for (int i = 0; i < communication._bitValues.Length; i++)
                {
                    sw.WriteLine(communication._fracValues[i]);
                }

                sw.Flush();
                sw.Close();
            }
        }

        static void SendAllOnesSimulator()
        {
            for (double t = 0.016; t <= 1.024; t *= 2)
            {
                var distance = 14;
                var symbolDuration = 0.060; // 60 msec
                var simduration = 1000000; //
                var numberOfMolecules = 1000;
                var halfLife = t;
                var lambda = Math.Log(2) / halfLife;
                var lookbackBits = 1;

                var communication = new Communication(simduration);
                var analytic = new Analytic(10, distance, 79.4, lambda);
                communication.InitializeAllOnes();

                CalculateArrivingMoleculesBinom(ref communication, analytic, symbolDuration, numberOfMolecules, lookbackBits);

                StreamWriter sw = new StreamWriter(String.Format("Logs\\AllOnes\\BinomSim D={0}, N={1}, Dec={2}, T_s={3}, Duration = {4}, Lookback = {5}, halfLife = {6}.txt", distance,
                   numberOfMolecules, true, symbolDuration, simduration, lookbackBits, halfLife), false);

                for (int i = 0; i < communication._bitValues.Length; i++)
                {
                    sw.WriteLine(communication._bitValues[i] + " " + communication._fracValues[i]);
                }

                sw.Flush();
                sw.Close();
            }
        }

        static void Main(string[] args)
        {
            RegularSimulatorEyeDiagram();
        }
    }
}
